<?php

abstract class HTML_Fluent_Element extends HTML_Fluent_Node {

    protected $parent;
    
    protected $factory;

    protected $attributes = array();

    protected $childNodes = array();

    abstract public function getName();

    public function __construct(HTML_Fluent_Factory $factory) {
        $this->factory = $factory;
    }

    public function __toString() {
        return $this->top()->serialize();
    }

    public function up($levels = 1) {
        $up = $this;
        for ($i = 0; $i < $levels; $i++) {
            $up = $up->getParent();
            if (!$up instanceof HTML_Fluent_Element) {
                throw new HTML_Fluent_Exception('No ancestor at that level');
            }
        }
        return $up;
    }

    public function setAttribute($name, $value) {
        if ($value === null) {
            return $this->removeAttribute($name);
        }
        $this->attributes[$name] = (string)$value;
        return $this;
    }

    public function removeAttribute($name) {
        $this->attributes[$name] = null;
        return $this;
    }

    public function getAttribute($name) {
        if (!isset($this->attributes[$name])) {
            return null;
        }
        return $this->attributes[$name];
    }

    public function getAttributes() {
        return $this->attributes;
    }

    public function newChildElement($elementName, $grandChildren = array()) {
        $element = $this->factory->newElement($elementName, $grandChildren);
        return $this->addChildElement($element);
    }

    public function addChildElement(HTML_Fluent_Element $element) {
        $element->setParent($this);
        $this->addChildNode($element);
        return $element;
    }

    public function addChildNode(HTML_Fluent_Node $node) {
        $this->childNodes[] = $node;
    }

    public function getParent() {
        return $this->parent;
    }

    public function setParent(HTML_Fluent_Element $parent) {
        $this->parent = $parent;
    }

    public function getChildNodes() {
        return $this->childNodes;
    }

    public function top() {
        $top = $this;
        while (1) {
            $parent = $top->getParent();
            if (!$parent) {
                return $top;
            }
            $top = $parent;
        }
    }

    public function out() {
        print $this->top()->serialize();
    }

    public function serialize() {
        $serializer = $this->factory->newSerializer();
        return $serializer->serialize($this);
    }
}
